package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"gopkg.in/mgo.v2/bson"
)

type Processor struct {
	dbo     *DBOperation
	cfg     *Config
	_idGps  bson.ObjectId
	_idTire bson.ObjectId
	rids    []string
}

func NewProcessor(dbo *DBOperation, cfg *Config) *Processor {
	p := Processor{dbo: dbo, cfg: cfg}
	return &p
}

func (p *Processor) Run() {

	if p.cfg.PushGPS {
		if bson.IsObjectIdHex(p.cfg.GpsId) {
			p._idGps = bson.ObjectIdHex(p.cfg.GpsId)
		}
		go p.processPosition()
	}

	if p.cfg.PushTireData {
		if bson.IsObjectIdHex(p.cfg.TireId) {
			p._idTire = bson.ObjectIdHex(p.cfg.TireId)
		}
		go p.processTire()
	}
}

func (p *Processor) processPosition() {
	zLog(nil, "启动读GPS数据协程")
	defer zLog(nil, "读GPS数据协程退出")

	var rids []string

	var res []GPSDataT
	var gpsSend GPSDataSendT

	for {

		rids = p.dbo.companyReceivers(p.cfg.CompanyId)
		if len(rids) == 0 {
			time.Sleep(time.Second)
			continue
		}
		res = p.dbo.newPosition(rids, p._idGps)
		if res == nil {
			time.Sleep(time.Second)
		} else {
			zLog(nil, "读到", len(res), "条新的GPS数据")
			for _, v := range res {
				p._idGps = v.Id
				gpsSend.fromData(v)
				gpsSend.Type = p.dbo.getRidType(gpsSend.SN)
				p.Post(p.cfg.GpsUrl, p._idGps, gpsSend)
			}
			p.cfg.GpsId = p._idGps.Hex()
			p.cfg.Save()
			time.Sleep(time.Millisecond * 100)
		}
	}
}

func (p *Processor) processTire() {
	zLog(nil, "启动读胎温胎压数据协程")
	defer zLog(nil, "读胎温胎压数据协程退出")

	var rids []string

	var res []TireDataRawT
	var tireSend TireDataSendT

	for {

		if p.cfg.TeamId != "" {
			rids = p.dbo.teamReceivers(p.cfg.TeamId)
		} else {
			rids = p.dbo.companyReceivers(p.cfg.CompanyId)
		}
		if len(rids) == 0 {
			time.Sleep(time.Second)
			continue
		}
		res = p.dbo.newTire(rids, p._idTire)
		if res == nil {
			time.Sleep(time.Second)
		} else {
			zLog(nil, "读到", len(res), "条新的胎温胎压数据")
			for _, v := range res {
				p._idTire = v.Id
				tireSend.fromTireRawData(&v)
				for _, v2 := range v.Tires {
					tireSend.fromTireData(&v2, p.cfg.NeedSensorLost)
					tireSend.Type = p.dbo.getRidType(tireSend.SN)
					// 胎温胎压推送标准按照如下执行：
					//TirePressure 胎压 数值在 0 < n < 22  才推送
					//Temperature 胎温 数值在 0 < n < 155 才推送
					if tireSend.TirePressure != 0.0 && tireSend.Temperature != 0 &&
						tireSend.TirePressure < 22 && tireSend.Temperature < 155 {
						p.Post(p.cfg.TireUrl, p._idTire, tireSend)
					}
				}
			}
			p.cfg.TireId = p._idTire.Hex()
			p.cfg.Save()
			time.Sleep(time.Millisecond * 100)
		}
	}
}

func dateTimeToUTC(d int, t int) string {
	year := d/10000 + 2000
	month := d % 10000 / 100
	day := d % 100
	hour := t / 10000
	min := t % 10000 / 100
	sec := t % 100

	ret := fmt.Sprintf("%d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, min, sec)
	return ret
}

type Result struct {
	Status string `json:"status"`
}

func (p *Processor) Post(url string, _id bson.ObjectId, v interface{}) {
	buf, _ := json.Marshal(v)
	s := string(buf)
	zLog(nil, "id:", _id.Hex(), " 推送：", s)

	req, _ := http.NewRequest("POST", url, bytes.NewBuffer(buf))
	req.Header.Add("Content-Type", "application/json")

	for _, v := range p.cfg.HttpHeaders {
		req.Header.Add(v.Key, v.Value)
	}

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		zLog(nil, "id:", _id.Hex(), "推送失败：", err.Error())
		return
	}

	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	zLog(nil, "id:", _id.Hex(), "返回结果：", string(body))
}

type GPSDataSendT struct {
	SN            string
	Type          int
	DeviceUTCTime string
	Latitude      float64
	Longitude     float64
	Speed         float64
	Course        int
	DeviceBattery float64
	PowerSource   int
	Flag          string `json:"flag"` // 1-GPS定位数据 2-胎温胎压数据
}

type GPSDataAddT struct {
	PowerSource int     `bson:"ps"`
	Vol         float64 `bson:"vol"`
}
type GPSDataT struct {
	Id        bson.ObjectId `bson:"_id"`
	Date      int           `bson:"date"`
	Time      int           `bson:"time"`
	SN        string        `bson:"rid"`
	Latitude  float64       `bson:"lat"`
	Longitude float64       `bson:"lng"`
	Speed     float64       `bson:"speed"`
	Course    int           `bson:"dir"`
	Add       GPSDataAddT   `bson:"add"`
}

func (p *GPSDataSendT) fromData(data GPSDataT) {
	p.SN = data.SN
	p.DeviceUTCTime = dateTimeToUTC(data.Date, data.Time)
	p.Latitude = data.Latitude
	p.Longitude = data.Longitude
	p.Speed = data.Speed
	p.Course = data.Course
	p.DeviceBattery = data.Add.Vol
	p.PowerSource = data.Add.PowerSource
	p.Flag = "1" // 1-GPS定位数据
}

type TireDataSendT struct {
	SN            string
	Type          int
	SensorID      string
	DeviceUTCTime string
	Latitude      float32
	Longitude     float32
	TirePressure  float32
	Temperature   int
	SensorBattery float32
	WheelPlace    string
	TireStatus    string
	Flag          string `json:"flag"` // 1-GPS定位数据 2-胎温胎压数据
}

func (p *TireDataSendT) fromTireRawData(data *TireDataRawT) {
	p.SN = data.Rid
	p.DeviceUTCTime = dateTimeToUTC(data.Date, data.Time)
	p.Latitude = data.Latitude
	p.Longitude = data.Longitude
	p.Flag = "2" // 2-胎温胎压数据
}

func (p *TireDataSendT) fromTireData(data *TireDataT, needSensorLost bool) {
	p.SensorID = data.SensorId
	p.SensorBattery = data.Voltage
	p.Temperature = data.Temperature
	p.TirePressure = data.Presure
	p.WheelPlace = fmt.Sprintf("%d-%d", data.CardId, data.SerialNum)
	var status []string = make([]string, 0)

	if data.PresureStatus == 1 {

		status = append(status, "161")
	}
	if data.PresureHighOrLow == 1 {

		status = append(status, "165")
	} else if data.PresureHighOrLow == 2 {

		status = append(status, "163")
	}
	if data.TemperatureStatus == 1 {

		status = append(status, "166")
	}
	if data.NoData == 1 && needSensorLost {

		status = append(status, "168")
	}
	if len(status) == 0 {
		p.TireStatus = "160"
	} else {
		p.TireStatus = strings.Join(status, ",")
	}
}
